import React, { PureComponent } from "react"
import update from "react/lib/update"
const { Children: { map }, cloneElement: clone } = React
import PropTypes from "prop-types"
import cls from "classnames"
import { CLASS_PREFIX as cp } from "../../const"
import { read } from "../utils/readwirte"
import "./styles.less"

export default class HYitem extends PureComponent {
  static isFormItem = true
  state = {
    error: null
  }
  itemChange = (value, data = {}, i) => {
    let { name, modelChange, model, multiterm, multChild } = this.props
    if (modelChange) {
      if (multChild) {
        modelChange(
          name,
          update(read(model, name) || [], {
            [i]: {
              $set: value
            }
          })
        )
      } else {
        modelChange(name, value)
      }
      if (multiterm) {
        for (let [k, v] of Object.entries(data)) {
          setTimeout(() => modelChange(k, v), 0)
        }
      }
    }
  }
  onChange = (e = window.event) => {
    let { onChange } = this.props
    let { value } = e.target
    this.itemChange(value)
    if (onChange) onChange(e, value)
  }
  createChange(children, i) {
    if (!children.type || typeof children.type === "string") {
      return {}
    }
    let { type, props: { onChange } } = children
    let { onChange: change, name, model } = this.props
    switch (type) {
    case "PickerWrapper": {
      return {
        onChange: (date, dateString) => {
          this.itemChange(dateString, {}, i)
          if (onChange) onChange(date, read(model, name))
          if (change) change(date, read(model, name))
        }
      }
    }
    default: {
      return {
        onChange: (e = window.event, data) => {
          let value = e ? (e.target ? e.target.value : e) : null
          this.itemChange(value, data, i)
          if (onChange) onChange(e, read(model, name))
          if (change) change(e, read(model, name))
        }
      }
    }
    }
  }
  getValue = (i) => {
    const { model, name, multChild, onChange } = this.props,
      value = read(model, name)
    if (multChild) return value && value[i]
    if (onChange) onChange(value)
    return value
  }
  cloneItemChild = (childrens, index = -1) => {
    if (!childrens) return
    const { injectModel, model, multChildItem } = this.props
    let num = 0
    return map(childrens, (child) => {
      if (child) {
        const { type, props } = child
        if (type && props) {
          if (typeof type === "string") {
            const Node = clone(
              child,
              {},
              this.cloneItemChild(props.children, index + num)
            )
            num += props.children.reduce ? props.children.reduce((x, { type, props }) => {
              return (type && props && typeof type !== "string") ? x + 1 : x
            }, 0) : 0
            return Node
          }
          const { defaultValue } = props
          if (defaultValue !== undefined) {
            if (this.defaultValue) {
              if (this.defaultValue !== defaultValue) {
                this.defaultValue = defaultValue
                this.updateValue()
              }
            } else {
              this.defaultValue = defaultValue
            }
          }
          ++index
          if (multChildItem) {
            multChildItem.length = index
          }
          if (type.isFormItem) {
            return clone(
              child,
              {
                ...this.createChange(child, index),
                ...(injectModel ? { model } : {}),
                value: props.value || this.getValue(index)
              },
              this.cloneItemChild(props.children, index + num)
            )
          }

          return clone(
            child,
            {
              ...this.createChange(child, index),
              ...(injectModel ? { model } : {}),
              value: props.value || this.getValue(index),
            }
          )
        }
      }
      return child
    })
  }
  cloneChildren = () => {
    const { children, injectModel, model, component } = this.props
    const modelProps = injectModel ? { model } : {}
    if (children) {
      return this.cloneItemChild(children)
    }

    if (component) {
      let createChange = this.createChange(component)
      return clone(component, {
        ...createChange,
        value: component.props.value || this.getValue(),
        ...modelProps
      })
    }
  }
  getValidateInfo = () => {
    const { validateInfo, error: E, message } = this.props
    return validateInfo && (E ? <E>{message || validateInfo.message}</E> : message || validateInfo.message)
  }
  render() {
    const { label, name, cs, isRequire } = this.props,
      message = this.getValidateInfo()
    return (
      <div
        className={cls(`${cp}-item`, cs)}>
        <div
          className={`${cp}-item-warp`}>
          {label === false ? null : <div
            className={`${cp}-item-label`}>{isRequire ? <span>*</span> : ""}{label || name}</div>}
          <div
            className={cls(`${cp}-item-content`, {"item-error-border": message})}>{this.cloneChildren()}</div>
        </div>
        <div
          className={`${cp}-item-error`}>{message}</div>
      </div>
    )
  }
  updateValue() {
    if (this.defaultValue) {
      const { name, modelChange } = this.props
      if (modelChange) {
        setTimeout(() => modelChange(name, this.defaultValue), 0)
      }
    }
  }
  componentDidMount() {
    this.updateValue()
  }
  static propTypes = {
    name: PropTypes.string.isRequired,  //字段名（model的键），必传
    label: PropTypes.oneOfType([
      PropTypes.oneOf([false]),         //不显示标签
      PropTypes.string,                 //显示标签的类容
      PropTypes.node,
      PropTypes.element
    ]),
    validator: PropTypes.arrayOf(       //校验规则,会校验Item内所有输入控件
      PropTypes.oneOfType([
        PropTypes.string,               //已内置校验
        PropTypes.func,                 //自定义函数校验,函数会接收输入控件的value,如果校验通过return null， 未通过return {message：提示文字}
        PropTypes.object                //{rules: 校验正则， message：提示文字}
      ])
    ),
    multChild: PropTypes.bool,          //是否允许有多个子输入控件
    multiterm: PropTypes.bool,          //是否允许自输入控件返回多个值
    onChange: PropTypes.func,           //子输入控件值改变是执行回调
    cs: PropTypes.oneOfType([           //指定容器class
      PropTypes.string,
      PropTypes.object
    ]),
    exclude: PropTypes.array            //忽略form上的验证
  }
}
